#include <iostream>
#include <set>

#include "city.hpp"
#include "foreach.hpp"
#include "merchant.hpp"
#include "occupation.hpp"

const_item_type_ptr best_item_to_trade(const money_market& exporter, const money_market& importer, int* profit_margin)
{
	std::set<const_item_type_ptr> items_exported, items_imported;
	exporter.goods_traded(items_exported);
	importer.goods_traded(items_imported);

	const_item_type_ptr result;
	int best_profit = 0;
	foreach(const const_item_type_ptr& i, items_exported) {
		if(items_imported.count(i) == 0) {
			continue;
		}

		int buy_price = exporter.can_buy_at(i);
		if(buy_price <= 0) {
			continue;
		}

		buy_price += exporter.tax_on_sale(buy_price);

		int sell_price = importer.can_sell_at(i);
		if(sell_price <= 0) {
			continue;
		}

		sell_price -= importer.tax_on_sale(sell_price);
		const int profit = sell_price - buy_price;
		if(profit > best_profit) {
			std::cerr << "buy price: " << buy_price << "; sell price: " << sell_price << "\n";
			best_profit = profit;
			result = i;
		}
	}

	if(profit_margin) {
		*profit_margin = best_profit;
	}

	return result;
}

std::ostream& operator<<(std::ostream& os, const merchant_trade& trade)
{
	os << "TRADE(" << trade.from->name() << " -> " << trade.to->name() << "; ITEM: " << trade.item->id() << "; PROFIT: " << trade.profit << "; MOVES: " << trade.moves_taken << ")";
	return os;
}

bool merchant_choose_trade(const world& w, const_city_ptr from, int nmoves, merchant_trade& result)
{
	result.profit = 0;
	result.moves_taken = 1;
	foreach(const_city_ptr to, w.cities()) {
		if(from == to) {
			continue;
		}

		const std::vector<hex::location>* trade_path;
		const int moves_taken = from->get_shortest_trade_path(w, to, &trade_path);
		if(moves_taken <= 0 || moves_taken > nmoves) {
			continue;
		}

		std::cerr << "takes " << moves_taken << " moves from " << from->name() << " -> " << to->name() << "\n";

		int profit;
		const_item_type_ptr item = best_item_to_trade(from->market(), to->market(), &profit);
		if(item && (profit*100)/moves_taken > (result.profit*100)/result.moves_taken) {
			std::cerr << "best item to trade: " << item->id() << "\n";
			result.profit = profit;
			result.item = item;
			result.moves_taken = moves_taken;
			result.from = from;
			result.to = to;
			result.path = trade_path;
		}
	}

	return result.profit != 0;
}

int estimate_merchant_profits(const world& w, const_city_ptr from, int nmoves)
{
	int result = 0;
	std::cerr << "ESTIMATING MERCHANT PROFITS FOR " << from->name() << ":\n";
	merchant_trade trade;
	while(nmoves > 0 && merchant_choose_trade(w, from, nmoves, trade)) {
		std::cerr << "  " << trade << "\n";
		nmoves -= trade.moves_taken;
		result += trade.profit;
	}

	std::cerr << "DONE TRADES, " << nmoves << " LEFT; TOTAL PROFIT: " << result << "\n";
	return result;
}

namespace {

struct merchant {
	citizen_ptr citizen;
	city_ptr city;
	int moves_left;
};

bool merchant_done(const merchant& m) { return m.moves_left == -1; }

void execute_trade(world& w, citizen_ptr trader, const merchant_trade& trade) {
	assert(w.map().is_loc_on_map(trade.from->tile().loc()));
	assert(w.map().is_loc_on_map(trade.to->tile().loc()));
	hex::tile_ptr t1 = w.map().get_tile(trade.from->tile().loc());
	hex::tile_ptr t2 = w.map().get_tile(trade.to->tile().loc());
	
	assert(t1->city());
	assert(t2->city());

	money_market& m1 = t1->city()->market();
	money_market& m2 = t2->city()->market();

	const int buy = m1.can_buy_at(trade.item);
	const int sell = m2.can_sell_at(trade.item);

	const int sell_taxes = m2.tax_on_sale(sell);

	const int profit = sell - sell_taxes - buy;
	std::cerr << "TRADE: " << sell << " - " << sell_taxes << " - " << buy << " = " << profit << "\n";
	if(profit >= 0) {
		if(m1.buy_item(trade.item, buy)) {
			m2.sell_item(trade.item, sell);
			t2->city()->owner()->get_money(sell_taxes);
			trader->get_item("silver", profit);

			std::cerr << "TRADING: " << trade << "\n";
			t1->city()->add_export(trade);
			t2->city()->add_import(trade);
		}
	}
}
}

std::vector<merchant_trade> execute_merchant_trades(world& w)
{
	std::cerr << "EXECUTING TRADES...\n";
	std::vector<merchant> merchants;
	foreach(city_ptr c, w.cities()) {
		foreach(citizen_ptr citizen, c->tile().citizens()) {
			if(citizen->get_occupation() == NULL || citizen->get_occupation() != merchant_occupation::instance()) {
				continue;
			}

			merchant m = { citizen, c, 500 };
			merchants.push_back(m);
		}
	}

	std::random_shuffle(merchants.begin(), merchants.end());

	std::cerr << "MERCHANTS: " << merchants.size() << "\n";
	std::vector<merchant_trade> result;
	while(std::count_if(merchants.begin(), merchants.end(), merchant_done) != merchants.size()) {
		foreach(merchant& m, merchants) {
			if(m.moves_left == -1) {
				continue;
			}

			std::cerr << "LOOKING FOR TRADE: " << m.moves_left << "\n";
			merchant_trade trade;
			if(merchant_choose_trade(w, m.city, m.moves_left, trade)) {
				std::cerr << "FOUND TRADE: " << trade << "\n";
				execute_trade(w, m.citizen, trade);
				m.moves_left -= trade.moves_taken;
				result.push_back(trade);
			} else {
				std::cerr << "NO TRADE\n";
				m.moves_left = -1;
			}
		}
	}

	return result;
}
